//   OData .NET Libraries
//   Copyright (c) Microsoft Corporation. All rights reserved.  
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at

//       http://www.apache.org/licenses/LICENSE-2.0

//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

#if !INTERNAL_DROP || ODATALIB

namespace Microsoft.OData.Core.UriParser.Visitors
{
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.OData.Core.UriParser.Semantic;

    /// <summary>
    /// Split Last Segment Of Certain Kind, the result of this handler is two part of ODataPath,
    /// FirstPart contains original ODataPath with out ending segments of type T,
    /// LastPart contains the ending segments of type T.
    /// </summary>
    /// <typeparam name="T">The type of ODataPathSegment to split</typeparam>
    internal sealed class SplitEndingSegmentOfTypeHandler<T> : PathSegmentHandler where T : ODataPathSegment
    {
        /// <summary>
        /// Queue contianing first part of ODataPath segment
        /// </summary>
        private readonly Queue<ODataPathSegment> first;

        /// <summary>
        /// Queue contianing last part of ODataPath segment
        /// </summary>
        private readonly Queue<ODataPathSegment> last;

        /// <summary>
        /// Default constructor
        /// </summary>
        public SplitEndingSegmentOfTypeHandler()
        {
            this.first = new Queue<ODataPathSegment>();
            this.last = new Queue<ODataPathSegment>();
        }

        /// <summary>
        /// The first part of split ODataPath
        /// </summary>
        public ODataPath FirstPart
        {
            get
            {
                return new ODataPath(this.first);
            }
        }

        /// <summary>
        /// The last part of split ODataPath
        /// </summary>
        public ODataPath LastPart
        {
            get
            {
                return new ODataPath(this.last);
            }
        }

        /// <summary>
        /// Handle a TypeSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(TypeSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a NavigationPropertySegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(NavigationPropertySegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle an EntitySetSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(EntitySetSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle an SingletonSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(SingletonSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a KeySegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(KeySegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a PropertySegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(PropertySegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle an OperationSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(OperationImportSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle an OperationSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(OperationSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle an OpenPropertySegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(OpenPropertySegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a CountSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(CountSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a LinksSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(NavigationPropertyLinkSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a ValueSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(ValueSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a BatchSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(BatchSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a BatchReferenceSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(BatchReferenceSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Handle a MetadataSegment
        /// </summary>
        /// <param name="segment">the segment to Handle</param>
        public override void Handle(MetadataSegment segment)
        {
            CommonHandler(segment);
        }

        /// <summary>
        /// Common handler to handle segments
        /// </summary>
        /// <param name="segment">The segment to deal with</param>
        private void CommonHandler(ODataPathSegment segment)
        {
            if (segment is T)
            {
                this.last.Enqueue(segment);
            }
            else
            {
                while (last.Any())
                {
                    this.first.Enqueue(this.last.Dequeue());
                }

                this.first.Enqueue(segment);
            }
        }
    }
}

#endif
